#include "Inode.h"

#include "IndirectionEen.h"
#include "IndirectionTwee.h"
#include "IndirectionDrie.h"
#include "Indirection.h"

Inode::Inode(FileSystem* filesystem, byte * data, int number) : filesystem(filesystem) {
	mode		 = getshort(data, 0);
	nLinks		 = getshort(data, 2);
	filesize 	 = getlong (data, 8);
	this->number = number;
	usecount	 = 1;

	l3tol( blockList, data + 12, 13 );
}

Inode::~Inode() {
}

short Inode::getType() {
	return mode;
}

short Inode::getMode() {
	return mode & 07777;
}

short Inode::getNumberOfLinks() {
	return nLinks;
}

long* Inode::getBlockList() {
	return blockList;
}

long Inode::getFileSize(){
	return filesize;
}

long Inode::getBlocknummer(long position) {
	assert(position % DBLKSIZ == 0);
	assert(position >= 0);
	int block_position = position / DBLKSIZ;

	// init positions
	const int direct = 10;
	const int indirection1_position = direct + INDSIZ;
	const int indirection2_position = indirection1_position + INDSIZ * INDSIZ;
	const int indirection3_position = indirection2_position + INDSIZ * INDSIZ * INDSIZ;

	// controleer of momenteel positie in block 1 tot 10 is
	if (block_position < direct) {
		// is een datablock
		return blockList[block_position];
	}
	else if (block_position < indirection1_position) {
		// is een indirection
		Blok* indirection1_block = filesystem->getBlok(blockList[10]);

		byte* bytes_temp = indirection1_block->getBytes();
		byte bytes[DBLKSIZ];
		for (int i = 0; i < DBLKSIZ; ++i) {
			bytes[i] = bytes_temp[i];
		}

		IndirectionEen* indirection1 = new IndirectionEen(filesystem, bytes);
        long blocknummer = indirection1->getBlocknummer(block_position - direct);

   		//delete bytes_temp;
        delete indirection1;
        indirection1 = 0;

        return blocknummer;
	}
	else if (block_position < indirection2_position) {
		// indirection 2
		Blok* indirection2_block = filesystem->getBlok(blockList[11]);
		
		byte* bytes_temp = indirection2_block->getBytes();
		byte bytes[DBLKSIZ];
		for (int i = 0; i < DBLKSIZ; ++i) {
			bytes[i] = bytes_temp[i];
		}

		IndirectionTwee* indirection2 = new IndirectionTwee(filesystem, bytes);
		long blocknummer = indirection2->getBlocknummer(block_position - indirection1_position);

   		//delete bytes_temp;
        delete indirection2;
        indirection2 = 0;

        return blocknummer;
	}
	else if (block_position < indirection3_position) {
		// indirection 3
		Blok* indirection3_block = filesystem->getBlok(blockList[12]);
		
		byte* bytes_temp = indirection3_block->getBytes();
		byte bytes[DBLKSIZ];
		for (int i = 0; i < DBLKSIZ; ++i) {
			bytes[i] = bytes_temp[i];
		}

		IndirectionDrie* indirection3 = new IndirectionDrie(filesystem, bytes);
		long blocknummer = indirection3->getBlocknummer(block_position - indirection2_position);

   		//delete bytes_temp;
        delete indirection3;
        indirection3 = 0;

        return blocknummer;
	}
	
	return -1;
}

int Inode::getNumber() {
	return number;
}

void Inode::toString() {
	printf("mode: %07o \n", getMode() );

	for (int i = 0; i < 13; i++)
		std::cout << "blocklist " << i << ": " << blockList[i] << std::endl;
}

int Inode::getUsecount() {
	return usecount;
}

void Inode::usecountIncrement() {
	usecount++;
}

void Inode::release() {
	assert(usecount > 0);
	usecount--;
}
